package com.xiekong.rpc.server.web;

import com.xiekong.rpc.bean.RpcRequest;
import com.xiekong.rpc.bean.RpcResponse;
import com.xiekong.rpc.codec.RpcDecoder;
import com.xiekong.rpc.codec.RpcEncoder;
import com.xiekong.rpc.handler.RpcServerHandler;
import com.xiekong.rpc.registry.ServiceRegistry;
import com.xiekong.rpc.server.RpcServer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Netty RPC服务器 用于web容器
 *
 * @author XieKong
 */
public class WebRpcServer extends RpcServer {
    private static final Logger logger = LoggerFactory.getLogger(WebRpcServer.class);

    public WebRpcServer(String serviceAddress, ServiceRegistry serviceRegistry) {
        super(serviceAddress, serviceRegistry);
    }

    @Override
    public void startup() {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup);
            /**
             *  BACKLOG用于构造服务端套接字ServerSocket对象，
             *  标识当服务器请求处理线程全满时，用于临时存放已
             *  完成三次握手的请求的队列的最大长度。如果未设置
             *  或所设置的值小于1，Java将使用默认值50。
             */
            bootstrap.option(ChannelOption.SO_BACKLOG, 1024)
                    /**
                     * 是否启用心跳保活机制。在双方TCP套接字
                     * 建立连接后（即都进入ESTABLISHED状态）
                     * 并且在两个小时左右上层没有任何数据传
                     * 输的情况下，这套机制才会被激活。
                     */
//                    .childOption(ChannelOption.SO_KEEPALIVE, false)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
//                            //解码器
//                            ch.pipeline().addLast(new DelimiterBasedFrameDecoder(Integer.MAX_VALUE, Delimiters.lineDelimiter()[0]));
//                            ch.pipeline().addLast(new StringDecoder());
//                            /**
//                             * 心跳包
//                             * 第一个参数表示读操作空闲时间
//                             * 第二个参数表示写操作空闲时间
//                             * 第三个参数表示读写操作空闲时间
//                             * 第四个参数单位
//                             */
//                            ch.pipeline().addLast(new IdleStateHandler(60, 45, 20, TimeUnit.SECONDS));
//                            //handler
//                            ch.pipeline().addLast(new SimpleServerHandler());
                            // 解码RPC请求
                            ch.pipeline().addLast(new RpcDecoder(RpcRequest.class));
                            // 处理RPC请求
                            ch.pipeline().addLast(new RpcServerHandler(getServiceMap()));
                            // 编码RPC响应
                            ch.pipeline().addLast(new RpcEncoder(RpcResponse.class));
                        }
                    });

            // 获取RPC服务器的IP地址与端口号
            String[] addressArray = StringUtils.split(getServiceAddress(), ":");
            String host = addressArray[0];
            int port = Integer.parseInt(addressArray[1]);
            // 启动RPC服务器
            ChannelFuture future = bootstrap.bind(host, port).sync();

            // 注册RPC服务地址
            for (String interfaceName : getServiceMap().keySet()) {
                getServiceRegistry().register(interfaceName, getServiceAddress());
            }
            //等待客户端通道关闭
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("", e);
        }
        finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
